{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 图像美化\n",
    "\n",
    "1. 图片恢复\n",
    "4. 亮度增强\n",
    "5. 磨皮美白\n",
    "6. 图像滤波\n",
    "\n",
    "图片的美化涉及到颜色等级的直方图统计特征，图片美白，图片滤镜(比如噪声的去除)。这些效果可以直接在美颜相机中看到使用案例。  \n",
    "图片美化主要涉及到颜色、亮度、修复、滤波和磨皮美白等效果。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 图片修复\n",
    "\n",
    "存在污损的图片进行图像修复时，需要一个模板图片，就是下面的黑色图片。这个图片需要尽可能的描述待修复区域，比如这里的白色十字交叉线。\n",
    "\n",
    "<img src='images/图片修复.jpg' width=50%>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-1"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 生成损坏的图片\n",
    "import cv2 \n",
    "import numpy as np\n",
    "img = cv2.imread('image0.jpg',1)\n",
    "for i in range(200,300): # 画竖线\n",
    "    img[i,200] = (255,255,255)\n",
    "    img[i,200+1] = (255,255,255)\n",
    "    img[i,200-1] = (255,255,255)\n",
    "for i in range(150,250): # 画横线\n",
    "    img[250,i] = (255,255,255)\n",
    "    img[250+1,i] = (255,255,255)\n",
    "    img[250-1,i] = (255,255,255)\n",
    "cv2.imwrite('damaged.jpg',img)\n",
    "cv2.imshow('image',img)\n",
    "cv2.waitKey(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-1"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 1 坏图 2 array 3 inpaint\n",
    "import cv2 \n",
    "import numpy as np\n",
    "img = cv2.imread('damaged.jpg',1)\n",
    "cv2.imshow('src',img)\n",
    "imgInfo = img.shape\n",
    "height = imgInfo[0]\n",
    "width = imgInfo[1]\n",
    "\n",
    "# paint用于描述损坏的部分\n",
    "paint = np.zeros((height,width,1),np.uint8)\n",
    "for i in range(200,300):\n",
    "    paint[i,200] = 255\n",
    "    paint[i,200+1] = 255\n",
    "    paint[i,200-1] = 255\n",
    "for i in range(150,250):\n",
    "    paint[250,i] = 255\n",
    "    paint[250+1,i] = 255\n",
    "    paint[250-1,i] = 255\n",
    "cv2.imshow('paint',paint)\n",
    "\n",
    "#1 src 2 mask\n",
    "imgDst = cv2.inpaint(img, paint, 3, cv2.INPAINT_TELEA)\n",
    "\n",
    "cv2.imshow('image',imgDst)\n",
    "cv2.waitKey(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 亮度增强\n",
    "\n",
    "亮度增强是最简单的美白算法，最简单的图像美化效果，比如将当前的像素值加上一个常量，或者将当前的像素值乘以一个放大系数。\n",
    "\n",
    "<img src='images/亮度增强.jpg' width=50%>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wall time: 2.74 s\n"
     ]
    }
   ],
   "source": [
    "# p = p+40\n",
    "import cv2\n",
    "import numpy as np\n",
    "img = cv2.imread('image0.jpg',1)\n",
    "imgInfo = img.shape\n",
    "height = imgInfo[0]\n",
    "width = imgInfo[1]\n",
    "cv2.imshow('src',img)\n",
    "\n",
    "# dst = np.zeros((height,width,3), np.uint8)\n",
    "# for i in range(0,height):\n",
    "#     for j in range(0,width):\n",
    "#         dst[i,j] = np.minimum(255, img[i,j].astype(np.int)+40)\n",
    "\n",
    "# minimum是防止像素值超过255\n",
    "dst = np.minimum(255, img.astype(np.int)+40)\n",
    "# 图像数据类型为必须为uint8，否则生成的图像全黑\n",
    "dst = dst.astype(np.uint8)\n",
    "\n",
    "cv2.imshow('dst',dst)\n",
    "cv2.waitKey(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-1"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# p = p*1.2+10\n",
    "import cv2\n",
    "import numpy as np\n",
    "img = cv2.imread('image0.jpg',1)\n",
    "imgInfo = img.shape\n",
    "height = imgInfo[0]\n",
    "width = imgInfo[1]\n",
    "cv2.imshow('src',img)\n",
    "dst = np.zeros((height,width,3), np.uint8)\n",
    "\n",
    "for i in range(0,height):\n",
    "    for j in range(0,width):\n",
    "        (b,g,r) = img[i,j]\n",
    "        bb = np.minimum(255, int(b*1.3)+10)\n",
    "        gg = np.minimum(255, int(g*1.2)+15)\n",
    "        dst[i,j] = (bb,gg,r)\n",
    "\n",
    "cv2.imshow('dst',dst)\n",
    "cv2.waitKey(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 磨皮美白\n",
    "\n",
    "可以用于祛斑，仍保留了图像的细节，使用**双边滤波器**来实现。\n",
    "\n",
    "<img src='images/磨皮美白.jpg' width=50%>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-1"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#双边滤波\n",
    "import cv2\n",
    "img = cv2.imread('1.png',1)\n",
    "cv2.imshow('src',img)\n",
    "# 双边滤波器有两个滤波内核，一个去除噪声，一个保持细节\n",
    "# 1.图像 2.邻域直径 3.颜色标准差 4.空间标准差\n",
    "# dst = cv2.bilateralFilter(img,15,35,35)\n",
    "\n",
    "blurred = np.hstack([cv2.bilateralFilter(img, 9, 21, 21),\n",
    "                     cv2.bilateralFilter(img,13, 31, 31),\n",
    "                     cv2.bilateralFilter(img,17, 41, 41)\n",
    "                     ])\n",
    "\n",
    "cv2.imshow(\"Bilateral\",blurred)\n",
    "cv2.waitKey(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 滤波操作\n",
    "\n",
    "在图像采集和生成中会不可避免的引入噪声，图像噪声是指存在于图像数据中的不必要的或多余的干扰信息，这对图像信息的提取造成干扰，所以要进行去噪声处理，常见的去除噪声的方法有均值滤波、中值滤波、高斯滤波等。这几个滤波的主要区别就是核函数不同。\n",
    "\n",
    "滤波和边缘检测比较类似，使用滤波核与图像进行卷积操作来实现图像的滤波。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 高斯滤波\n",
    "\n",
    "滤波核就是通过高斯滤波函数进行生成的。用当前的像素点和滤波核进行相乘（矩阵对应点相乘）并求和，这也是图像卷积的概念。\n",
    "\n",
    "<img src='images/图像卷积.jpg' width=50%>\n",
    "\n",
    "上图中滤波核中的所有值总和为16，所以计算结束后还需要除以16，类似于平均。整个滤波效果越往中间值越大，越往周围值越小，这是二维高斯函数的特点。\n",
    "\n",
    "下面是一维高斯函数和二维高斯函数的图：  \n",
    "<img src='images/一维高斯.jpg' width=50%>\n",
    "<img src='images/二维高斯.jpg' width=50%>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 高斯滤波，缺点是图像变得非常模糊\n",
    "import cv2\n",
    "import numpy as np\n",
    "\n",
    "# 原图有很多椒盐噪声\n",
    "img = cv2.imread('image11.jpg',1)\n",
    "dst = cv2.GaussianBlur(img, (5,5), 1.5) # 二维高斯函数在X和Y方向上的标准差都是1.5\n",
    "# dst = cv2.medianBlur(img, 5) # 中值滤波\n",
    "# dst = cv2.blur(img, (5,5)) # 均值滤波\n",
    "# cv2.boxFilter() # 方框滤波\n",
    "\n",
    "while(1):\n",
    "    cv2.imshow('src',img)\n",
    "    cv2.imshow('dst',dst)\n",
    "    k=cv2.waitKey(0)\n",
    "    if k == ord('q'): # 按q键退出\n",
    "        break\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**均值滤波**是图像处理中最常用的手段，从频率域观点来看均值滤波是一种低通滤波器，高频信号将会去掉，因此可以帮助消除图像尖锐噪声，实现图像平滑，模糊等功能。理想的均值滤波是用每个像素和它周围像素计算出来的平均值替换图像中每个像素。采样Kernel数据通常是3X3的矩阵，如下表示：\n",
    "<img src='images/均值滤波.png' width=30%>\n",
    "\n",
    "从左到右从上到下计算图像中的每个像素，最终得到处理后的图像。均值滤波可以加上两个参数，即迭代次数和Kernel大小。一个相同的Kernel，但是多次迭代就会效果越来越好。同样，迭代次数相同，Kernel矩阵越大，均值滤波的效果就越明显。\n",
    "\n",
    "可以看到处理后的图片变模糊了一些，这是因为均值滤波就是将图像做平滑处理，像素值高的像素会被拉低，像素值低像素会被拉高，趋向于一个平均值，所以图像会变模糊一些。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-1"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#均值滤波 卷积核5*5，其中的值全为1\n",
    "import cv2\n",
    "import numpy as np\n",
    "img = cv2.imread('image11.jpg',1)\n",
    "imgInfo = img.shape\n",
    "height = imgInfo[0]\n",
    "width = imgInfo[1]\n",
    "dst = np.zeros((height,width,3),np.uint8)\n",
    "\n",
    "for i in range(2, height-2):\n",
    "    for j in range(2, width-2):\n",
    "        # 其本身加上邻域像素\n",
    "        b,g,r = np.split(img[i-2:i+3, j-2:j+3], 3, axis=2)\n",
    "        b = np.uint8(np.sum(b) / 25) # uint32 -> uint8\n",
    "        g = np.uint8(np.sum(g) / 25)\n",
    "        r = np.uint8(np.sum(r) / 25)\n",
    "        dst[i,j] = (b,g,r)\n",
    "\n",
    "cv2.imshow('src',img)\n",
    "cv2.imshow('dst',dst)\n",
    "cv2.waitKey(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-1"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 中值滤波 3*3\n",
    "import cv2\n",
    "import numpy as np\n",
    "img = cv2.imread('image11.jpg',1)\n",
    "imgInfo = img.shape\n",
    "height = imgInfo[0]\n",
    "width = imgInfo[1]\n",
    "img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n",
    "dst = np.zeros((height,width,3), np.uint8)\n",
    "collect = np.zeros(9, np.uint8)\n",
    "\n",
    "for i in range(1,height-1):\n",
    "    for j in range(1,width-1):\n",
    "        kernel = img[i-1:i+2, j-1:j+2]\n",
    "        collect = np.reshape(kernel,9) # 降维\n",
    "        dst[i,j] = sorted(collect)[4]\n",
    "cv2.imshow('src',img)\n",
    "cv2.imshow('dst',dst)\n",
    "cv2.waitKey(0)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
